Исследование пользователей в мобильном приложении¶

Описание проекта: Я аналитик в мобильном приложении по продаже продуктов питания. Дизайнеры захотели поменять шрифты во всём приложении, а менеджеры испугались, что пользователям будет непривычно. Договорились принять решение по результатам A/A/B-теста. Пользователей разбили на 3 группы: 2 контрольные со старыми шрифтами и одну экспериментальную — с новыми.

Описание данных Каждая запись в логе — это действие пользователя, или событие:

  • EventName — название события;
  • DeviceIDHash — уникальный идентификатор пользователя;
  • EventTimestamp — время события;
  • ExpId — номер эксперимента: 246 и 247 — контрольные группы, а 248 — экспериментальная.

Цель исследования: Нужно изучить воронку продаж, ответить на вопросы, исследовать результаты А/А/В эксперимента и выявить какой шрифт лучше.

Ход исследования:

  • Подготовка данных: загрузка данных и изучение общей информации.
  • Предобработка данных: приведение названия столбцов к нижнему регистру и к нужному типу, обработка явных дубликатов.
  • Исследовательский анализ данных: описание и визуализирование общей информации о пользователях, событиях и группах.
  • Проверка гипотез о равенстве долей: сформулирование нулевой и альтернативной гипотезы, их проверка и описание выводов.

Общий вывод: резюмирование полученных результатов, формулировка ключевых выводов.

Шаг 1. Откройте файл с данными и изучите общую информацию¶

In [1]:
# импортируем библиотеки pandas, matplot, numpy, scipy
import pandas as pd
import numpy as np
import seaborn as sns
import datetime as dt
from matplotlib import pyplot as plt
from scipy import stats as st
import math as mth
import plotly.express as px

# устанавливаем отображение количества столбцов
pd.options.display.max_columns = 40

# устанавливаем отображение полного текста в ячейке
pd.set_option('display.max_colwidth', None)

# устанавливаем отображение чисел с двумя знаками после запятой
pd.set_option('display.float_format', '{:.2f}'.format)
In [2]:
# открываем файл
logs = pd.read_csv('/Users/ildushisamov/Desktop/projects/sborka_2/logs_exp.csv', sep='\t')
In [3]:
def my_func(x):
    print('-'*15, 'Исходный датафрейм', '-'*15)
    display(x.head())
    print('')
    print('')
    print('-'*15, 'Общая информация о датафрейме', '-'*15)
    print('')
    print('')
    x.info()
    print('-'*15, 'Количество явных дубликатов в датафрейме', '-'*15)
    display(x.duplicated().sum())
    print('')
    print('')
    print('-'*15, 'Описательная статистика', '-'*15)
    display(x.describe())
    
my_func(logs)
--------------- Исходный датафрейм ---------------
EventName DeviceIDHash EventTimestamp ExpId
0 MainScreenAppear 4575588528974610257 1564029816 246
1 MainScreenAppear 7416695313311560658 1564053102 246
2 PaymentScreenSuccessful 3518123091307005509 1564054127 248
3 CartScreenAppear 3518123091307005509 1564054127 248
4 PaymentScreenSuccessful 6217807653094995999 1564055322 248

--------------- Общая информация о датафрейме ---------------


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244126 entries, 0 to 244125
Data columns (total 4 columns):
 #   Column          Non-Null Count   Dtype 
---  ------          --------------   ----- 
 0   EventName       244126 non-null  object
 1   DeviceIDHash    244126 non-null  int64 
 2   EventTimestamp  244126 non-null  int64 
 3   ExpId           244126 non-null  int64 
dtypes: int64(3), object(1)
memory usage: 7.5+ MB
--------------- Количество явных дубликатов в датафрейме ---------------
413

--------------- Описательная статистика ---------------
DeviceIDHash EventTimestamp ExpId
count 244126.00 244126.00 244126.00
mean 4627568124591259648.00 1564913915.84 247.02
std 2642424998963707904.00 177134.32 0.82
min 6888746892508752.00 1564029816.00 246.00
25% 2372212476992240640.00 1564756580.25 246.00
50% 4623191541214045184.00 1564919395.00 247.00
75% 6932517045703054336.00 1565074511.00 248.00
max 9222603179720523776.00 1565212517.00 248.00

В таблице 244126 записей (вместе с шапкой таблицы) и 4 колонки:

  • EventName — название события;
  • DeviceIDHash — уникальный идентификатор пользователя;
  • EventTimestamp — время события;
  • ExpId — номер эксперимента: 246 и 247 — контрольные группы, а 248 — экспериментальная.

Пропусков нет.

Нужно удалить явные дубликаты.

Переименуем названия столбцов, поменяем тип данных колонки EventTimestamp на date_time.

Шаг 2. Подготовьте данные¶

In [4]:
# переименуем столбцы
logs.columns = ['event_name', 'user_id', 'event_time', 'group']

# поменяем типы данных
logs['event_time'] = pd.to_datetime(logs['event_time'], unit='s')

# добавим столбец даты и времени
logs['date_time'] = logs['event_time'].dt.date

# удалим дубликаты
logs = logs.drop_duplicates()

display(logs.duplicated().sum())
display(logs)
logs.info()
0
event_name user_id event_time group date_time
0 MainScreenAppear 4575588528974610257 2019-07-25 04:43:36 246 2019-07-25
1 MainScreenAppear 7416695313311560658 2019-07-25 11:11:42 246 2019-07-25
2 PaymentScreenSuccessful 3518123091307005509 2019-07-25 11:28:47 248 2019-07-25
3 CartScreenAppear 3518123091307005509 2019-07-25 11:28:47 248 2019-07-25
4 PaymentScreenSuccessful 6217807653094995999 2019-07-25 11:48:42 248 2019-07-25
... ... ... ... ... ...
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

243713 rows × 5 columns

<class 'pandas.core.frame.DataFrame'>
Index: 243713 entries, 0 to 244125
Data columns (total 5 columns):
 #   Column      Non-Null Count   Dtype         
---  ------      --------------   -----         
 0   event_name  243713 non-null  object        
 1   user_id     243713 non-null  int64         
 2   event_time  243713 non-null  datetime64[ns]
 3   group       243713 non-null  int64         
 4   date_time   243713 non-null  object        
dtypes: datetime64[ns](1), int64(2), object(2)
memory usage: 11.2+ MB
In [5]:
# проверим сколько пользователей вошли в разные группы одновременно
logs.groupby('user_id').agg({'group' : 'nunique'}).query('group > 1').count()
Out[5]:
group    0
dtype: int64

Таких пользователей нет.

Шаг 3. Изучите и проверьте данные¶

In [6]:
# посмотрим все события в логе
logs['event_name'].drop_duplicates()
Out[6]:
0            MainScreenAppear
2     PaymentScreenSuccessful
3            CartScreenAppear
6          OffersScreenAppear
29                   Tutorial
Name: event_name, dtype: object
In [7]:
# посчитаем количество пользователей
logs['user_id'].drop_duplicates().count()
Out[7]:
7551
In [8]:
# посчитаем среднее количество событий на пользователя
logs.groupby('user_id').agg({'event_name': 'count'}).mean()
Out[8]:
event_name   32.28
dtype: float64
In [9]:
# выведем все даты, за которые у нас есть данные
logs['date_time'].drop_duplicates()
Out[9]:
0         2019-07-25
9         2019-07-26
40        2019-07-27
95        2019-07-28
200       2019-07-29
384       2019-07-30
797       2019-07-31
2828      2019-08-01
39057     2019-08-02
74663     2019-08-03
108014    2019-08-04
141047    2019-08-05
177160    2019-08-06
212982    2019-08-07
Name: date_time, dtype: object
In [10]:
# выведем минимальную и максимальную дату
print('Минимальная дата:', logs['date_time'].min())
print('Максимальная дата:', logs['date_time'].max())
Минимальная дата: 2019-07-25
Максимальная дата: 2019-08-07
In [11]:
# построим столбчатую диаграмму по количеству событий в зависимости от времени в разрезе групп
(logs
 .pivot_table(index='date_time', values='event_name', columns='group', aggfunc='count')
 .plot(kind='bar', figsize=(15, 10))
)

# Add labels
plt.title('Диаграмма количества событий по датам')
plt.xlabel('Дата')
plt.ylabel('Количество событий')
plt.show()

По диаграмме видно, что в первую неделю данные не полные. Как будто до 2019-08-01 приложением пользовались не все пользователи из наших выборок, а лишь некоторая их часть.\ Поэтому уберем данные за 1 неделю и построим ещё раз данную диаграмму уже с "чистыми" данными.

In [12]:
# посчитаем количество уникальных пользователей до "очищения"
cnt_users_before = logs['user_id'].nunique()

# посчитаем количество событий до "очищения"
cnt_events_before = logs['user_id'].count()

# "очистим" наши данные, взяв данные за период от 1 августа (включительно)
logs = logs.query('event_time >= "2019-08-01"')

# посчитаем изменения количества уникальных пользователей  после "очистки"
print('Потеря уникальных пользователей в относительных значениях составляет:', '{:.3%}'.format(1 - (logs['user_id'].nunique() / cnt_users_before)))
print('Потеря уникальных пользователей в абсолютных значениях составляет:', cnt_users_before - logs['user_id'].nunique())
print()

# посчитаем изменения количества событий  после "очистки"
print('Потеря событий в относительных значениях составляет:', '{:.3%}'.format(1 - (logs['user_id'].count() / cnt_events_before)))
print('Потеря событий в абсолютных значениях составляет:', cnt_events_before - logs['user_id'].count())
Потеря уникальных пользователей в относительных значениях составляет: 0.225%
Потеря уникальных пользователей в абсолютных значениях составляет: 17

Потеря событий в относительных значениях составляет: 1.160%
Потеря событий в абсолютных значениях составляет: 2826

В процентном соотношении после очистки данных теряется лишь около 1,2% данных, что в рамках допустимого.\ Далее попробуем построить диаграмму из очищенных данных.

In [13]:
# построим столбчатую диаграмму по количеству событий в зависимости от времени в разрезе групп по очищенным данным
(logs
 .pivot_table(index='date_time', values='event_name', columns='group', aggfunc='count')
 .plot(kind='bar', figsize=(15, 10))
)

# Add labels
plt.title('Диаграмма количества событий по датам')
plt.xlabel('Дата')
plt.ylabel('Количество событий')
plt.show()

Теперь можно сказать, что у нас полные данные за весь период с 1 августа 2019 года по 7 августа 2019 года.

Шаг 4. Изучите воронку событий¶

In [14]:
logs
Out[14]:
event_name user_id event_time group date_time
2828 Tutorial 3737462046622621720 2019-08-01 00:07:28 246 2019-08-01
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
... ... ... ... ... ...
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

240887 rows × 5 columns

In [15]:
# посмотрим, какие события есть в логах и как часто они встречаются. Отсортируем события по частоте.
cnt_events = logs.groupby('event_name').agg({'user_id': 'nunique'})
cnt_events.columns = ['cnt_users']
cnt_events.sort_values(by='cnt_users', ascending=False)
Out[15]:
cnt_users
event_name
MainScreenAppear 7419
OffersScreenAppear 4593
CartScreenAppear 3734
PaymentScreenSuccessful 3539
Tutorial 840
In [16]:
# посчитаем, сколько пользователей совершали каждое из этих событий. Отсортируем события по числу пользователей.
cnt_events = logs.groupby('event_name').agg({'user_id': 'nunique', 'event_name': 'count'})
cnt_events.columns = ['cnt_users', 'cnt_events']
cnt_events.sort_values(by='cnt_users', ascending=False)
Out[16]:
cnt_users cnt_events
event_name
MainScreenAppear 7419 117328
OffersScreenAppear 4593 46333
CartScreenAppear 3734 42303
PaymentScreenSuccessful 3539 33918
Tutorial 840 1005
In [17]:
# посчитаем долю пользователей, которые совершили действия по данному событию
cnt_events['ratio, %'] = cnt_events['cnt_users'] / logs['user_id'].nunique() * 100
cnt_events = cnt_events.sort_values(by='cnt_users', ascending=False)
cnt_events
Out[17]:
cnt_users cnt_events ratio, %
event_name
MainScreenAppear 7419 117328 98.47
OffersScreenAppear 4593 46333 60.96
CartScreenAppear 3734 42303 49.56
PaymentScreenSuccessful 3539 33918 46.97
Tutorial 840 1005 11.15

Скорее всего в самом начале появляется экран с обучением, которое можно пропустить. Именно поэтому так мало пользователей с его прохождением, либо тут есть баг. Мне кажется стоит исключить эти данные.

  • Tutorial (Обучение)

Далее открывается главный экран

  • MainScreenAppear (Появился главный экран)

Дальше в определенный момент появляется экран с оффером

  • OffersScreenAppear (Появляется экран с предложениями)

И если у человека есть желание, то он проходит далее к экрану оплаты

  • CartScreenAppear (Появится экран корзины)

Следующий экран выходит после успешной оплаты

  • PaymentScreenSuccessful (Платеж успешно выполнен)
In [18]:
# исключаем события Tutorial из наших данных 
cnt_events = cnt_events.query('event_name != "Tutorial"')
logs = logs.query('event_name != "Tutorial"')
display(cnt_events)
logs
cnt_users cnt_events ratio, %
event_name
MainScreenAppear 7419 117328 98.47
OffersScreenAppear 4593 46333 60.96
CartScreenAppear 3734 42303 49.56
PaymentScreenSuccessful 3539 33918 46.97
Out[18]:
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
... ... ... ... ... ...
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

239882 rows × 5 columns

По воронке событий посчитайте, какая доля пользователей проходит на следующий шаг воронки (от числа пользователей на предыдущем). То есть для последовательности событий A → B → C посчитайте отношение числа пользователей с событием B к количеству пользователей с событием A, а также отношение числа пользователей с событием C к количеству пользователей с событием B.

In [19]:
# посчитаем конверсию пользователей от события к событию
cnt_events['conversion, %'] = (cnt_events['cnt_users'] / cnt_events['cnt_users'].shift(1))
cnt_events
/var/folders/m0/hqhjn4g943v5qdg7cp126_600000gn/T/ipykernel_3993/3460090568.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  cnt_events['conversion, %'] = (cnt_events['cnt_users'] / cnt_events['cnt_users'].shift(1))
Out[19]:
cnt_users cnt_events ratio, % conversion, %
event_name
MainScreenAppear 7419 117328 98.47 NaN
OffersScreenAppear 4593 46333 60.96 0.62
CartScreenAppear 3734 42303 49.56 0.81
PaymentScreenSuccessful 3539 33918 46.97 0.95

На втором шаге при переходе с главной страницы на страницу с оффером теряется 38% пользователей. Возможно проблема, в том, что не у всех пользователей выходит предложение с оффером или условия вывода страницы оффера затруднительны для среднего пользователя.

In [20]:
# посчитаем долю пользователей, которые доходят с главной страницы до успешной оплаты
cnt_events['funnel, %'] = (cnt_events['cnt_users'] / cnt_events['cnt_users'].values[0])
cnt_events
/var/folders/m0/hqhjn4g943v5qdg7cp126_600000gn/T/ipykernel_3993/1152550264.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  cnt_events['funnel, %'] = (cnt_events['cnt_users'] / cnt_events['cnt_users'].values[0])
Out[20]:
cnt_users cnt_events ratio, % conversion, % funnel, %
event_name
MainScreenAppear 7419 117328 98.47 NaN 1.00
OffersScreenAppear 4593 46333 60.96 0.62 0.62
CartScreenAppear 3734 42303 49.56 0.81 0.50
PaymentScreenSuccessful 3539 33918 46.97 0.95 0.48

48% пользователей доходят до успешной оплаты.

In [21]:
# визуализируем нашу воронку с помощью графика
stages = cnt_events.index

df_cnt_events = pd.DataFrame(dict(number=cnt_events['cnt_events'], stage=stages))
df_cnt_events['data'] = 'События'

df_cnt_users = pd.DataFrame(dict(number=cnt_events['cnt_users'], stage=stages))
df_cnt_users['data'] = 'Уникальные пользователи'

df = pd.concat([df_cnt_users], axis=0)
fig = px.funnel(df, x='number', y='stage', color='data')
fig.update_layout(title='Воронка', title_x = 0.5)
fig.show()

Шаг 5. Изучите результаты эксперимента¶

In [22]:
# посчитаем количество пользователей в каждой группе
logs.groupby('group').agg({'user_id': 'nunique', 'event_name': 'count'})
Out[22]:
user_id event_name
group
246 2483 78985
247 2512 76684
248 2535 84213
In [23]:
# посчитаем количество пользователей которые совершали действия по событиям и разделим их по группам
(logs
 .pivot_table(index='event_name', values='user_id', columns='group', aggfunc='count')
 .sort_values(by=246, ascending=False)
)
Out[23]:
group 246 247 248
event_name
MainScreenAppear 37676 39090 40562
OffersScreenAppear 14767 15179 16387
CartScreenAppear 14690 12434 15179
PaymentScreenSuccessful 11852 9981 12085
In [24]:
# посчитаем количество уникальных пользователей которые совершали действия по событиям и разделим их по группам
(logs
 .pivot_table(index='event_name', values='user_id', columns='group', aggfunc='nunique')
 .sort_values(by=246, ascending=False)
)
Out[24]:
group 246 247 248
event_name
MainScreenAppear 2450 2476 2493
OffersScreenAppear 1542 1520 1531
CartScreenAppear 1266 1238 1230
PaymentScreenSuccessful 1200 1158 1181

Тестов довольно много и нужно сделать коррекцию уровней значимости, чем ниже и займусь.

In [25]:
# сделаем коррекцию уровня значимости по методу Бонферрони
m = 16 # число тестов
alpha = 0.05 # исходный уровень значимости

# считаем поправку
alpha_bonf = alpha / m # добавьте код для расчёта коррекции уровня значимости здесь

# считаем FWER
fwer_bonf = 1 - (1 - alpha_bonf)**(m) # добавьте код для расчёта FWER здесь

print(fwer_bonf)
0.048845042569365726
In [26]:
# сделаем коррекцию уровня значимости по методу Шидака
m = 16 # число тестов
alpha = 0.05 # исходный уровень значимости

# считаем поправку
alpha_sidak = 1 - (1 - alpha)**(1/m)# добавьте код для расчёта коррекции уровня значимости здесь

# считаем FWER
fwer_sidak = 1 - (1 - alpha_sidak) ** m

print(fwer_sidak)
0.04999999999999993

Если брать скорректированный уровень значимости по методам Бонферрони или Шидака, то изменений в тестах не будет, т.к. стандартный уровень значимости 0.05 примерно такой же как и скорректированный по этим двум методам.

In [27]:
# сделаем коррекцию уровня значимости по методу Холма
m, alpha = 16, 0.05 # число тестов и уровень значимости
result = []
for i in range(m): # получаем номер текущего теста и считаем коррекцию 
    result += [alpha / (m - i)]
print(result)
[0.003125, 0.0033333333333333335, 0.0035714285714285718, 0.0038461538461538464, 0.004166666666666667, 0.004545454545454546, 0.005, 0.005555555555555556, 0.00625, 0.0071428571428571435, 0.008333333333333333, 0.01, 0.0125, 0.016666666666666666, 0.025, 0.05]

Скорректированный уровень значимости по методу Холма может привнести другой эффект на наши тесты. Используем в наших тестах скорректированные уровни значимости по методу Холма.

In [28]:
# создадим функцию для проверки гипотезы о равенстве долей

# alpha = 0.05 # критический уровень статистической значимости

def z_test(group1, group2, event_name, alpha):
    
    group_1 = logs[logs['group'] == group1]
    group_2 = logs[logs['group'] == group2]
    display('group_1:', group_1)
    display('group_2:',group_2)
    
    successes = np.array([group_1[group_1['event_name'] == event_name]['user_id'].nunique(),
                          group_2[group_2['event_name'] == event_name]['user_id'].nunique()])
    print('successes:', successes)
    
    trials = np.array([group_1['user_id'].nunique(),
                       group_2['user_id'].nunique()])
    print('trials:', trials)
    print()
    
    # пропорция успехов в первой группе:
    p1 = successes[0] / trials[0]
    print('Конверсия в первой группе равна:', '{0:.2%}'.format(p1))
    
    # пропорция успехов во второй группе:
    p2 = successes[1] / trials[1]
    print('Конверсия во второй группе равна:', '{0:.2%}'.format(p2))
    print()
    # пропорция успехов в комбинированном датасете:
    p_combined = (successes[0] + successes[1]) / (trials[0] + trials[1])
    print('p_combined:', p_combined)
    # разница пропорций в датасетах
    difference = p1 - p2 
    print('difference:', difference)
    assert((p_combined * (1 - p_combined) * (1 / trials[0] + 1 / trials[1])) > 0)
    
    # считаем статистику в ст.отклонениях стандартного нормального распределения
    z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1 / trials[0] + 1 / trials[1]))
    print('z_value:', z_value)
    # задаем стандартное нормальное распределение (среднее 0, ст.отклонение 1)
    distr = st.norm(0, 1)

    p_value = (1 - distr.cdf(abs(z_value))) * 2
    
    print()
    print('p-значение: ', p_value)
    print()
    
    if p_value < alpha:
        print('p-value < alpha, поэтому нужно отвергнуть нулевую гипотезу.')
        print('Есть основания говорить, что между долями есть существенные различия.')
    else:
        print('p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.') 
        print('Есть основания говорить, что между долями нет существенных различий.')

Сформируем нулевую и альтернативную гипотезы:

  • H0 - Нет различий между долями
  • H1 - Есть различия между долями
In [29]:
# проверим гипотезу о равенстве долей (между группами 246 и 247 по событию посещения главной страницы)
# и посчитаем находят ли статистические критерии различия между ними
z_test(246, 247, "MainScreenAppear", 0.003125)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

successes: [2450 2476]
trials: [2483 2512]

Конверсия в первой группе равна: 98.67%
Конверсия во второй группе равна: 98.57%

p_combined: 0.9861861861861861
difference: 0.0010408356441637956
z_value: 0.31512026353759287

p-значение:  0.7526703436483038

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [30]:
# проверим гипотезу о равенстве долей (между группами 246 и 247 по событию получения оффера)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(246, 247, "OffersScreenAppear", 0.0033333333333333335)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

successes: [1542 1520]
trials: [2483 2512]

Конверсия в первой группе равна: 62.10%
Конверсия во второй группе равна: 60.51%

p_combined: 0.613013013013013
difference: 0.015927414700216302
z_value: 1.155560508362548

p-значение:  0.24786096925282264

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [31]:
# проверим гипотезу о равенстве долей (между группами 246 и 247 по событию оплаты картой)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(246, 247, "CartScreenAppear", 0.0035714285714285718)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

successes: [1266 1238]
trials: [2483 2512]

Конверсия в первой группе равна: 50.99%
Конверсия во второй группе равна: 49.28%

p_combined: 0.5013013013013013
difference: 0.017032701350072288
z_value: 1.203775303516804

p-значение:  0.22867643757335676

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [32]:
# проверим гипотезу о равенстве долей (между группами 246 и 247 по событию успешной покупки)
# и посчитаем находят ли статистические критерии различия между ними
z_test(246, 247, "PaymentScreenSuccessful", 0.0038461538461538464)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

successes: [1200 1158]
trials: [2483 2512]

Конверсия в первой группе равна: 48.33%
Конверсия во второй группе равна: 46.10%

p_combined: 0.4720720720720721
difference: 0.02229908601419589
z_value: 1.578432523853264

p-значение:  0.11446627829276612

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [33]:
# выделим самое популярное событие и посчитаем число пользователей, совершивших это событие в каждой из групп
pop_events = (logs
 .pivot_table(index='event_name', values='user_id', columns='group', aggfunc='nunique')
 .sort_values(by=246, ascending=False)
)
pop_events.head(1)
Out[33]:
group 246 247 248
event_name
MainScreenAppear 2450 2476 2493

Самым популярным событием является конечно самое первое событие которое видят пользователи. Это открытие главного экрана приложения.

In [34]:
# посчитаем количество уникальных пользователей в каждой группе
cnt_users = (logs
 .groupby('group')
 .agg({'user_id': 'nunique'})
)
cnt_users
Out[34]:
user_id
group
246 2483
247 2512
248 2535
In [35]:
# посчитаем различие в процентном соотношении между минимальным и максимальным количеством пользователей в группе
100 - (cnt_users['user_id'].min() / cnt_users['user_id'].max() * 100)
Out[35]:
2.051282051282058

Различие между группами составляет 2%.

In [36]:
# посчитаем долю пользователей, совершивших событие в каждой из групп
# для этого разделим количество совершивших на общее количество
pop_events['ratio 246, %'] = pop_events[246] / logs[logs['group'] == 246]['user_id'].nunique()
pop_events['ratio 247, %'] = pop_events[247] / logs[logs['group'] == 247]['user_id'].nunique()
pop_events['ratio 248, %'] = pop_events[248] / logs[logs['group'] == 248]['user_id'].nunique()
pop_events
Out[36]:
group 246 247 248 ratio 246, % ratio 247, % ratio 248, %
event_name
MainScreenAppear 2450 2476 2493 0.99 0.99 0.98
OffersScreenAppear 1542 1520 1531 0.62 0.61 0.60
CartScreenAppear 1266 1238 1230 0.51 0.49 0.49
PaymentScreenSuccessful 1200 1158 1181 0.48 0.46 0.47

Получается, что с главного экрана до успешного платежа доходят 46-48% пользователей.

Далее проведем A\B тесты по группам (246 \ 248) и (247 \ 248).

In [37]:
# проверим гипотезу о равенстве долей (между группами 248 и 246 по событию посещения главной страницы)
# и посчитаем находят ли статистические критерии различия между ними
z_test(246, 248, "MainScreenAppear", 0.004545454545454546)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [2450 2493]
trials: [2483 2535]

Конверсия в первой группе равна: 98.67%
Конверсия во второй группе равна: 98.34%

p_combined: 0.9850538062973296
difference: 0.0032776727903590652
z_value: 0.9567144401185823

p-значение:  0.3387114076159288

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [38]:
# проверим гипотезу о равенстве долей (между группами 248 и 246 по событию получения оффера)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(246, 248, "OffersScreenAppear", 0.005555555555555556)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1542 1531]
trials: [2483 2535]

Конверсия в первой группе равна: 62.10%
Конверсия во второй группе равна: 60.39%

p_combined: 0.6123953766440813
difference: 0.01707818292594776
z_value: 1.2414900640949729

p-значение:  0.21442476639710506

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [39]:
# проверим гипотезу о равенстве долей (между группами 248 и 246 по событию оплаты картой)
# и посчитаем находят ли статистические критерии различия между ними
z_test(246, 248, "CartScreenAppear", 0.0071428571428571435)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1266 1230]
trials: [2483 2535]

Конверсия в первой группе равна: 50.99%
Конверсия во второй группе равна: 48.52%

p_combined: 0.49740932642487046
difference: 0.024659995662814904
z_value: 1.7467905524022547

p-значение:  0.08067367598823139

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [40]:
# проверим гипотезу о равенстве долей (между группами 248 и 246 по событию успешной покупки)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(246, 248, "PaymentScreenSuccessful", 0.01)
'group_1:'
event_name user_id event_time group date_time
2829 MainScreenAppear 3737462046622621720 2019-08-01 00:08:00 246 2019-08-01
2830 MainScreenAppear 3737462046622621720 2019-08-01 00:08:55 246 2019-08-01
2831 OffersScreenAppear 3737462046622621720 2019-08-01 00:08:58 246 2019-08-01
2834 OffersScreenAppear 3737462046622621720 2019-08-01 00:10:26 246 2019-08-01
2835 MainScreenAppear 3737462046622621720 2019-08-01 00:10:47 246 2019-08-01
... ... ... ... ... ...
244120 MainScreenAppear 5746969938801999050 2019-08-07 21:12:11 246 2019-08-07
244122 MainScreenAppear 5849806612437486590 2019-08-07 21:13:59 246 2019-08-07
244123 MainScreenAppear 5746969938801999050 2019-08-07 21:14:43 246 2019-08-07
244124 MainScreenAppear 5746969938801999050 2019-08-07 21:14:58 246 2019-08-07
244125 OffersScreenAppear 5746969938801999050 2019-08-07 21:15:17 246 2019-08-07

78985 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1200 1181]
trials: [2483 2535]

Конверсия в первой группе равна: 48.33%
Конверсия во второй группе равна: 46.59%

p_combined: 0.4744918294141092
difference: 0.017408635129134487
z_value: 1.234731791642485

p-значение:  0.21693033984516674

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [41]:
# проверим гипотезу о равенстве долей (между группами 248 и 247 по событию посещения главной страницы)
# и посчитаем находят ли статистические критерии различия между ними
z_test(247, 248, "MainScreenAppear", 0.004166666666666667)
'group_1:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [2476 2493]
trials: [2512 2535]

Конверсия в первой группе равна: 98.57%
Конверсия во второй группе равна: 98.34%

p_combined: 0.9845452744204478
difference: 0.0022368371461952696
z_value: 0.6441218302357536

p-значение:  0.5194964354051703

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [42]:
# проверим гипотезу о равенстве долей (между группами 248 и 247 по событию получения оффера)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(247, 248, "OffersScreenAppear", 0.005)
'group_1:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1520 1531]
trials: [2512 2535]

Конверсия в первой группе равна: 60.51%
Конверсия во второй группе равна: 60.39%

p_combined: 0.6045175351694075
difference: 0.0011507682257314578
z_value: 0.0835991653262869

p-значение:  0.9333751305879443

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [43]:
# проверим гипотезу о равенстве долей (между группами 248 и 247 по событию оплаты картой)
# и посчитаем находят ли статистические критерии различия между ними
z_test(247, 248, "CartScreenAppear", 0.00625)
'group_1:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1238 1230]
trials: [2512 2535]

Конверсия в первой группе равна: 49.28%
Конверсия во второй группе равна: 48.52%

p_combined: 0.4890033683376263
difference: 0.007627294312742616
z_value: 0.5419855498181188

p-значение:  0.5878284605111943

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [44]:
# проверим гипотезу о равенстве долей (между группами 248 и 247 по событию успешной покупки)
# и посчитаем находят ли статистические критерии различия между ними 
z_test(247, 248, "PaymentScreenSuccessful", 0.008333333333333333)
'group_1:'
event_name user_id event_time group date_time
2832 MainScreenAppear 1433840883824088890 2019-08-01 00:08:59 247 2019-08-01
2833 MainScreenAppear 4899590676214355127 2019-08-01 00:10:15 247 2019-08-01
2838 MainScreenAppear 4899590676214355127 2019-08-01 00:11:28 247 2019-08-01
2839 OffersScreenAppear 4899590676214355127 2019-08-01 00:11:30 247 2019-08-01
2843 OffersScreenAppear 4899590676214355127 2019-08-01 00:12:36 247 2019-08-01
... ... ... ... ... ...
244088 MainScreenAppear 2300292234025330845 2019-08-07 21:05:37 247 2019-08-07
244089 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:43 247 2019-08-07
244090 OffersScreenAppear 2300292234025330845 2019-08-07 21:05:54 247 2019-08-07
244091 MainScreenAppear 2300292234025330845 2019-08-07 21:05:57 247 2019-08-07
244121 MainScreenAppear 4599628364049201812 2019-08-07 21:12:25 247 2019-08-07

76684 rows × 5 columns

'group_2:'
event_name user_id event_time group date_time
2844 MainScreenAppear 4613461174774205834 2019-08-01 00:14:31 248 2019-08-01
2845 MainScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2846 CartScreenAppear 4613461174774205834 2019-08-01 00:14:34 248 2019-08-01
2847 PaymentScreenSuccessful 4613461174774205834 2019-08-01 00:14:43 248 2019-08-01
2848 OffersScreenAppear 4613461174774205834 2019-08-01 00:14:51 248 2019-08-01
... ... ... ... ... ...
244082 MainScreenAppear 8249271441488197538 2019-08-07 21:03:33 248 2019-08-07
244084 OffersScreenAppear 8249271441488197538 2019-08-07 21:03:43 248 2019-08-07
244109 MainScreenAppear 5317814261427487562 2019-08-07 21:09:22 248 2019-08-07
244110 OffersScreenAppear 5317814261427487562 2019-08-07 21:09:27 248 2019-08-07
244115 OffersScreenAppear 5317814261427487562 2019-08-07 21:10:08 248 2019-08-07

84213 rows × 5 columns

successes: [1158 1181]
trials: [2512 2535]

Конверсия в первой группе равна: 46.10%
Конверсия во второй группе равна: 46.59%

p_combined: 0.4634436298791361
difference: -0.004890450885061404
z_value: -0.3483572972992236

p-значение:  0.7275718682261119

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.

Далее проведем A+A\B тесты по группам (246+247 \ 248).

In [45]:
# создадим функцию для теста объединенных групп
def z_test_3group(group1, group2, group3, event_name, alpha):
    
    group_1 = pd.concat([logs[logs['group'] == group1], logs[logs['group'] == group2]])
    group_2 = logs[logs['group'] == group3]

    successes = np.array([group_1[group_1['event_name'] == event_name]['user_id'].nunique(),
                          group_2[group_2['event_name'] == event_name]['user_id'].nunique()])
    

    trials = np.array([group_1['user_id'].nunique(),
                       group_2['user_id'].nunique()])

    # пропорция успехов в первой группе:
    p1 = successes[0] / trials[0]
    print('Конверсия в первой группе равна:', '{0:.2%}'.format(p1))
    
    # пропорция успехов во второй группе:
    p2 = successes[1] / trials[1]
    print('Конверсия во второй группе равна:', '{0:.2%}'.format(p2))
    
    # пропорция успехов в комбинированном датасете:
    p_combined = (successes[0] + successes[1]) / (trials[0] + trials[1])
    
    # разница пропорций в датасетах
    difference = p1 - p2 
    
    assert((p_combined * (1 - p_combined) * (1 / trials[0] + 1 / trials[1])) > 0)
    
    # считаем статистику в ст.отклонениях стандартного нормального распределения
    z_value = difference / mth.sqrt(p_combined * (1 - p_combined) * (1 / trials[0] + 1 / trials[1]))

    # задаем стандартное нормальное распределение (среднее 0, ст.отклонение 1)
    distr = st.norm(0, 1)
    
    p_value = (1 - distr.cdf(abs(z_value))) * 2
    
    print()
    print('p-значение: ', '{0:.3f}'.format(p_value))
    print()
    
    if p_value < alpha:
        print('p-value < alpha, поэтому нужно отвергнуть нулевую гипотезу.')
        print('Есть основания говорить, что между долями есть существенные различия.')
    else:
        print('p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.') 
        print('Есть основания говорить, что между долями нет существенных различий.')
In [46]:
# проверим гипотезу о равенстве долей (между объединенными группами 246+247 и 248 по событию посещения главной страницы)
# и посчитаем находят ли статистические критерии различия между ними
z_test_3group(246, 247, 248, "MainScreenAppear", 0.0125)
Конверсия в первой группе равна: 98.62%
Конверсия во второй группе равна: 98.34%

p-значение:  0.349

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [47]:
# проверим гипотезу о равенстве долей (между объединенными группами 246+247 и 248 по событию получения оффера)
# и посчитаем находят ли статистические критерии различия между ними
z_test_3group(246, 247, 248, "OffersScreenAppear", 0.016666666666666666)
Конверсия в первой группе равна: 61.30%
Конверсия во второй группе равна: 60.39%

p-значение:  0.446

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [48]:
# проверим гипотезу о равенстве долей (между объединенными группами 246+247 и 248 по событию оплаты картой)
# и посчитаем находят ли статистические критерии различия между ними
z_test_3group(246, 247, 248, "CartScreenAppear", 0.025)
Конверсия в первой группе равна: 50.13%
Конверсия во второй группе равна: 48.52%

p-значение:  0.187

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.
In [49]:
# проверим гипотезу о равенстве долей (между объединенными группами 246+247 и 248 по событию успешной покупки)
# и посчитаем находят ли статистические критерии различия между ними 
z_test_3group(246, 247, 248, "PaymentScreenSuccessful", 0.05)
Конверсия в первой группе равна: 47.21%
Конверсия во второй группе равна: 46.59%

p-значение:  0.611

p-value > alpha, поэтому нельзя отвергнуть нулевую гипотезу.
Есть основания говорить, что между долями нет существенных различий.

В целом было проведено 16 статистических тестов:

A\A тесты:

  • между группами 246 и 247 - 4 теста

A\B тесты:

  • между группами 246 и 248 - 4 теста;
  • между группами 247 и 248 - 4 теста

A+A\B тесты:

  • между группами 246+247 и 248 - 4 теста.

Общий вывод¶

В данном проекте мы провели исследование о равенстве долей трех групп пользователей мобильного приложения по продаже продуктов питания. Пользователей разбили на 3 группы: 2 контрольные со старыми шрифтами и одну экспериментальную — с новыми, а также соединили пользователей 2х контрольных групп со старыми штрифтами в одну и сравнили доли с экспериментальной группой. Грубо говоря мы провели А+А/В тест.

В тестах, где сравнивались контрольные группы с экспериментальной, т.е. А/В или А+А/В результаты были следующими:

  • в А/А тестах: в 4 из 4 тестов между долями не было существенных различий;
  • в А/В тестах: в 8 из 8 тестов между долями не было существенных различий;
  • в А+А/В тестах: в 4 из 4 тестов между долями не было существенных различий.

Подытожив можно сказать, что из полученных результатов тестов между всеми группами нет существенных различий. Это говорит о том, что для наши пользователи из 3х групп - не видят существенной разницы между старым и новым шрифтом. В связи с чем предлагаю остановить тест и признать его неуспешным.

In [ ]: